//  Copyright (c) 2012 M.A. (Thijs) van den Berg, http://sitmo.com/
//
//  Use, modification and distribution are subject to the BOOST Software License. 
// (See accompanying file LICENSE.txt)

#ifndef STDFIN_RANDOM_LAGGED_XOR_ENGINE_HPP
#define STDFIN_RANDOM_LAGGED_XOR_ENGINE_HPP

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

namespace stdfin {

template<class Generator>
class lagged_xor_engine
{
public:
    typedef Generator base_type;
    typedef typename base_type::result_type result_type;

    // -------------------------------------------------
    // Constructors
    // -------------------------------------------------
    lagged_xor_engine() : _rng() { init(); }
    explicit lagged_xor_engine(const base_type& rng) : _rng(rng) { init(); }
    template<class T> explicit lagged_xor_engine(T& s) { _rng.seed(s); init(); }
    template<class It> lagged_xor_engine(It& first, It last) : _rng(first, last) { init(); }
    
    // -------------------------------------------------
    // Seeding
    // -------------------------------------------------
    void seed() {  _rng.seed(); init(); }
    void seed(const result_type& s) { _rng.seed(s); init(); }
    template<class Sseq> void seed(Sseq& q) { _rng.seed(q); init(); }
    template<class It> void seed(It& first, It last) { _rng.seed(first, last); init(); }

    // -------------------------------------------------
    // misc
    // -------------------------------------------------
    const base_type& base() const { return _rng; }
    static result_type (min)() { return (base_type::min)(); }
    static result_type (max)() { return (base_type::max)(); }
    void discard(unsigned long long z) { for(unsigned long long j=0; j<z; ++j) (*this)(); }
    bool operator!=(const lagged_xor_engine& a2) { return !(*this == a2); }
    bool operator==(const lagged_xor_engine& a2) { 
        return (_rng==a2._rng) && (_buf==a2._buf) && (_bufi==a2._bufi); 
    }

    // -------------------------------------------------
    // The actual random number generation
    // -------------------------------------------------
    inline result_type operator()() 
    {
        ++_bufi;
        if (_bufi == _buf.end()) _bufi = _buf.begin(); 
        *_bufi = ((*_bufi << 2) | (*_bufi >> (32-2))
        *_bufi  ^= _rng();
        return *_bufi;
    }

    // -------------------------------------------------
    // IO
    // -------------------------------------------------
    template<class CharT, class Traits>
    friend std::basic_ostream<CharT,Traits>&
    operator<<(std::basic_ostream<CharT,Traits>& os, const lagged_xor_engine& s) {
        os << s._rng << ' ' << s._buf.size() << ' ' << ( s._bufi - s._buf.begin() ) << ' ';
        std::copy(s._buf.begin(), s._buf.end(), std::ostream_iterator<result_type>(os, " "));
        return os;
    }

    template<class CharT, class Traits>
    friend std::basic_istream<CharT,Traits>&
    operator>>(std::basic_istream<CharT,Traits>& is, lagged_xor_engine& s) {
        is >> s._rng;
        std::size_t buf_size; is >> buf_size; 
        s._buf.resize( buf_size );
        for(s._bufi=s._buf.begin(); s._bufi!=s._buf.end(); ++s._bufi)
            is >> *s._bufi;
        is >> buf_size;
        s._bufi = s._buf.begin() + buf_size;
        return is;
    }

    // -------------------------------------------------
    // Init
    // -------------------------------------------------
    void init(std::size_t buf_size)
    {
        _buf.resize(buf_size);
        std::generate(_buf.begin(), _buf.end(), _rng);
        _bufi = _buf.begin();
    }

private:
    void init() { init(1); }

    base_type _rng;
    std::vector<result_type> _buf;
    typename std::vector<result_type>::iterator _bufi;
};

} // namespace stdfin
#endif